#include "Server.h"
#include <iostream>

using namespace std;

Server::Server()
{
	string znak, temp;
	MsgQueue::getInstance();

	//wczytaj hasla
	fstream plik;
	plik.open("puns.txt");
	if (!plik)
		perror("Blad otwarcia pliku z haslami");
	while (!plik.eof())
	{
		if ((znak = plik.get()) != "#")
		{
			temp += znak;
		}
		else
		{
			Puns.push_back(temp);
			temp.clear();
		}

	}
}

Server::~Server()
{
	vector<User*>::iterator it;
	vector<Game*>::iterator it2;

	for (it = Clients.begin(); it != Clients.end(); it++)
		delete (*it);

	for (it2 = Games.begin(); it2 != Games.end(); it2++)
		delete (*it2);
}

void Server::run()
{

	while (1)
	{

		// zdejmij z kolejki wiadomosc do obsluzenia
		Message m = MsgQueue::getInstance().popMessage();
		User * user = m.userPtr;

		if (user != NULL)
		{
			switch (m.pack->code)
			{

			//obsluz wiadomosc

			case C_LOGIN: // DONE
				cout << "LOGIN" << endl;
				login(user, ((ChatPackage*) m.pack)->data);
				break;

			case C_LOGOUT: // DONE
				//logout(user);
				break;

			case C_ROOM_LIST: // DONE
				roomList(user);
				break;

			case C_CREATE_ROOM: // DONE
				addRoom(user);
				break;

			case C_JOIN_ROOM: // DONE
				joinRoom(user, ((NumberPackage*) m.pack)->number);
				break;

			case C_LEAVE_ROOM: // DONE
				removePlayerFromGame(user);
				break;

			case C_START_NEW_GAME:
				startGame(user);
				break;

			case CHAT_MESSAGE:
				serveChatMessage(m.userPtr, ((ChatPackage*) m.pack)->data);
				break;

			case IMAGE_MESSAGE:
				serveImageMessage(m.userPtr,
						(static_cast<ImagePackage*>(m.pack))->imageBuffer);
				break;

			case C_GIVE_UP:
				userGaveUp(user);
				break;

				// kod wewnetrzny serwera - blad usera trzeba go usunac
			case 99:
				logout(user);
				break;

			default:
				cerr << "NIEZNANY TYP WIADOMOSCI" << endl;
				break;

			}
		}
		m.clean();
	}

}
;

void Server::serveImageMessage(User *user, char *image)
{

	Game * gra = findGameById(user->getGameId());
	if (gra != NULL)
		gra->sendToAllWithoutOne(IMAGE_MESSAGE, image, user);
}

void Server::serveChatMessage(User *user, string msg)
{
	if (user->getGameId() != 0)
	{
		Game * gra = findGameById(user->getGameId());
		if (gra != NULL)
		{
			gra->sendToAll(CHAT_MESSAGE, user->getLogin() + ": " + msg);
			if (gra->getStarted())	gra->checkIfGuessed(user, msg);
		}
	}
}

void Server::startGame(User *u)
{
	Game * gra = findGameById(u->getGameId());
	if (gra != NULL)
	{
		if (gra->isEnoughPlayersToStart())
		{
			gra->sendToAll(S_START_NEW_GAME_OK);
			gra->start();
		}
		else
			u->sendToClient(S_START_NEW_GAME_FALSE);
	}
}

//DONE
void Server::roomList(User *u)
{
	vector<Game*>::iterator it;
	ostringstream ss;

	for (it = Games.begin(); it != Games.end(); it++)
	{
		ss << (*it)->getId();
		ss << (*it)->howManyPlayers();
		ss << (int) ((*it)->getStarted());
	}

	u->sendToClient(S_ROOM_LIST, ss.str());
}

void Server::userGaveUp(User *user)
{
	if (user->inAnyGame() && user->getDrawer())
	{
		Game * ptr = findGameById(user->getGameId());
		ptr->sendToAll(CHAT_MESSAGE, "Uzytkownik " + user->getLogin() + " poddal sie");
		ptr->setStarted(false);
	}

}

User * Server::connectClient(int socket)
{

	Clients.push_back(new User(socket));
	Clients.back()->createSem();

	return Clients.back();
}

bool Server::isLoginFree(string login)
{
	vector<User*>::iterator it;

	for (it = Clients.begin(); it != Clients.end(); ++it)
		if ((*it)->getLogin() == login)
			return false;

	return true;
}

void Server::removePlayerFromGame(User *u)
{
	Game *ptr;

	if ((ptr = findGameById(u->getGameId())) != NULL)
	{
		ptr->removePlayer(u);

		if (u->getDrawer())
		{
			ptr->setStarted(false);
			ptr->sendToAll(CHAT_MESSAGE,
					"Rysujacy - " + u->getLogin() + ", opuscil gre.");
		}
		else
		{
			ptr->setStarted(false);
			ptr->sendToAll(CHAT_MESSAGE, u->getLogin() + ", opuscil gre");
			ptr->sendToAll(S_UPDATE_RANKING, ptr->createRankingMsg());
		}
	}

}

void Server::removeRoom(int gameId)
{
	vector<Game*>::iterator it;

	for (it = Games.begin(); it != Games.end(); ++it)
		if ((*it)->getId() == gameId)
		{
			(*it)->setStarted(false);
			Games.erase(it);
			return;
		}
}

void Server::addRoom(User *u)
{
	if(Games.size()<MAX_ROOMS){
		Game * g = new Game(&Puns);
		g->addPlayer(u);
		Games.push_back(g);

		u->sendToClient(S_ADD_ROOM_OK, g->getId());
		u->sendToClient(S_UPDATE_RANKING, g->createRankingMsg());
	}else u->sendToClient(S_ADD_ROOM_FALSE);
}

void Server::joinRoom(User *u, int gameId)
{
	Game *ptr;

	if ((ptr = findGameById(gameId)) != NULL && u->getGameId() == 0)
	{ /* je���li istnieje pok���j o danym id dodaj gracza, poinformuj pozosta���ych o do������czeniu nowego gracza */
		ptr->addPlayer(u);

		if (ptr->getStarted())
		{
			u->sendToClient(S_JOIN_ROOM_NOT_STARTED, ptr->getId());
			u->sendToClient(S_JOIN_ROOM_GAME_STARTED, ptr->getId());
		}
		else
			u->sendToClient(S_JOIN_ROOM_NOT_STARTED, ptr->getId());
		ptr->sendToAllWithoutOne(CHAT_MESSAGE,
				u->getLogin() + " dolaczyl do gry", u);
		ptr->sendToAll(S_UPDATE_RANKING, ptr->createRankingMsg());
	}
	else
	{ /* je���li nie istnieje pok���j o danym id lub gracz ma ju��� przypisany pok���j wy���lij komunikat do gracza o pora���ce */
		u->sendToClient(S_JOIN_ROOM_FALSE);
	}
}

User* Server::findUserById(int id_)
{
	vector<User*>::iterator it;

	for (it = Clients.begin(); it != Clients.end() && Clients.size() > 0; ++it)
		if ((*it)->getId() == id_)
			return (*it);

	return NULL;
}

Game* Server::findGameById(int id)
{
	vector<Game *>::iterator it;

	for (it = Games.begin(); it != Games.end() && Games.size() > 0; it++)
		if ((*it)->getId() == id)
			return (*it);

	return NULL;
}

// Kamil zalozyl, ze uzytkownika juz nie ma w zadnej grze jesli wyslany logout
void Server::logout(User *user)
{
	vector<User*>::iterator it;

	if (user->getGameId() != 0)
		removePlayerFromGame(user);

	for (it = Clients.begin(); it != Clients.end() && Clients.size() > 0; ++it)
		if ((*it) == user)
		{
			Clients.erase(it);
			delete (user);
			break;
		}
}

void Server::login(User *a, string login)
{
	if (isLoginFree(login))
	{
		a->setLogin(login);
		a->sendToClient(S_LOGIN_OK);
	}
	else		a->sendToClient(S_LOGIN_FALSE);
}

